
JSLoader = function() {
	var n = navigator;
	var dua = n.userAgent,
		dav = n.appVersion,
		tv = parseFloat(dav);

	if (dua.indexOf("Opera") >= 0) {
		this.isOpera = tv;
	}

	var isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0;
	var isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined;

	if (dua.indexOf("Gecko") >= 0 && !isKhtml && !isWebKit) {
		this.isMozilla = this.isMoz = tv;
	}
	if (this.isMoz) {
		this.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1] || dua.split("Shiretoko/")[1]) || undefined;
	}
	if (document.all && !this.isOpera) {
		this.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
		if(this.isIE >= 8 && document.documentMode != 5){
			this.isIE = document.documentMode;
		}
	}

	if(this.isIE && window.location.protocol === "file:"){
		this.config.ieForceActiveXXhr=true;
	}
};

JSLoader.prototype = {

	// private
	_scopeMap : {},
	_loadedModules : {},
	_modulePrefixes : {},
	_global_omit_module_check : false,
	_loadedUrls : [],
	_XMLHTTP_PROGIDS : ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],

	_getProp : function(/*Array*/parts, /*Boolean*/create, /*Object*/context) {
		var obj=context || this.global;
		for (var i = 0, p; obj && (p = parts[i]); i++) {
			if (i == 0 && this._scopeMap[p]) {
				p = this._scopeMap[p];
			}
			obj = (p in obj ? obj[p] : (create ? obj[p] = {} : undefined));
		}
		return obj;
	},

	_getObject : function(/*String*/name, /*Boolean*/create, /*Object*/context) {
		return this._getProp(name.split("."), create, context); // Object
	},

	_getModuleSymbols : function(/*String*/modulename) {
		var syms = modulename.split(".");
		for (var i = syms.length; i > 0; i--){
			var parentModule = syms.slice(0, i).join(".");
			if ((i == 1) && !this._moduleHasPrefix(parentModule)){		
				syms[0] = "../" + syms[0];
			} else {
				var parentModulePath = this._getModulePrefix(parentModule);
				if(parentModulePath != parentModule){
					syms.splice(0, i, parentModulePath);
					break;
				}
			}
		}
		return syms;
	},

	_moduleHasPrefix : function(/*String*/module) {
		var mp = this._modulePrefixes;
		return !!(mp[module] && mp[module].value);
	},

	_getModulePrefix : function(/*String*/module) {
		var mp = this._modulePrefixes;
		if (this._moduleHasPrefix(module)) {
			return mp[module].value;
		}
		return module;
	},

	_loadPath : function(/*String*/relpath, /*String?*/module, /*Function?*/cb) {
		var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : "") + relpath;
		try{
			return !module ? this._loadUri(uri, cb) : this._loadUriAndCheck(uri, module, cb); // Boolean
		}catch(e){
			console.error(e);
			return false;
		}
	},

	_loadUri : function(/*String*/uri, /*Function?*/cb) {
		if (this._loadedUrls[uri]) {
			return true;
		}
		var contents = this._getText(uri, true);
		if (!contents) {
			return false;
		}
		this._loadedUrls[uri] = true;
		this._loadedUrls.push(uri);
		if (cb) {
			contents = '(' + contents + ')';
		} else {
			contents = "" + contents + "";
		}
		if (this.isMoz) {
			contents += "\r\n//@ sourceURL=" + uri;
		}
		var value = eval(contents);
		if (cb) {
			cb(value);
		}
		return true;
	},

	_loadUriAndCheck : function(/*String*/uri, /*String*/moduleName, /*Function?*/cb) {
		var ok = false;
		try {
			ok = this._loadUri(uri, cb);
		} catch (e) {
			console.error("failed loading " + uri + " with error: " + e);
		}
		return !!(ok && this._loadedModules[moduleName]); // Boolean
	},

	_getText : function(/*URI*/ uri, /*Boolean*/ fail_ok) {

		var http = this._xhrObj();

		http.open('GET', uri, false);
		try{
			http.send(null);
			if (!this._isDocumentOk(http)) {
				var err = Error("Unable to load " + uri + " status:" +  http.status);
				err.status = http.status;
				err.responseText = http.responseText;
				throw err;
			}
		} catch (e) {
			if (fail_ok) {
				return null;
			}
			throw e;
		}
		return http.responseText; // String
	},

	_xhrObj : function() {
		var http, last_e;
		if (!this.isIE || !this.config.ieForceActiveXXhr){
			try {
				http = new XMLHttpRequest();
			} catch (e) {
			}
		}
		if (!http) {
			for (var i = 0; i < 3; ++i) {
				var progid = this._XMLHTTP_PROGIDS[i];
				try {
					http = new ActiveXObject(progid);
				} catch (e) {
					last_e = e;
				}

				if (http) {
					this._XMLHTTP_PROGIDS = [progid];
					break;
				}
			}
		}

		if (!http) {
			throw new Error("XMLHTTP not available: "+last_e);
		}

		return http;
	},

	_isDocumentOk : function(http) {
		var stat = http.status || 0;
		return (stat >= 200 && stat < 300) || stat == 304 || stat == 1223 || (!stat && (location.protocol=="file:" || location.protocol=="chrome:") );
	},

	// public
	config : {},

	global : window,

	registerModulePath : function(/*String*/module, /*String*/prefix) {
		this._modulePrefixes[module] = { name: module, value: prefix };
	},

	require : function(/*String*/moduleName, /*Boolean?*/omitModuleCheck) {
		omitModuleCheck = this._global_omit_module_check || omitModuleCheck;

		var module = this._loadedModules[moduleName];
		if (module) {
			return module;
		}

		var relpath = this._getModuleSymbols(moduleName).join("/") + '.js';

		var modArg = (!omitModuleCheck) ? moduleName : null;
		var ok = this._loadPath(relpath, modArg);

		if(!ok && !omitModuleCheck){
			throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
		}

		if(!omitModuleCheck){
			module = this._loadedModules[moduleName];
			if(!module){
				throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); 
			}
		}

		return module;
	},

	provide : function(/*String*/resourceName) {
		resourceName = resourceName + "";
		return (this._loadedModules[resourceName] = this._getObject(resourceName, true));
	}

};

if (typeof jsloader == "undefined") {
	(function(){
		jsloader = new JSLoader();
	})();
};